package com.leizi.igym.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leizi.igym.constant.*;
import com.leizi.igym.domain.Trainer;
import com.leizi.igym.domain.TrainerCategory;
import com.leizi.igym.dto.TrainerAddDTO;
import com.leizi.igym.dto.TrainerDTO;
import com.leizi.igym.dto.TrainerPageQueryDTO;
import com.leizi.igym.enums.ReturnCodeEnum;
import com.leizi.igym.exception.BusinessException;
import com.leizi.igym.mapper.TrainerMapper;
import com.leizi.igym.service.CategoryService;
import com.leizi.igym.service.TrainerCategoryService;
import com.leizi.igym.service.TrainerService;
import com.leizi.igym.util.RedisUtils;
import com.leizi.igym.utils.DateQWUtils;
import com.leizi.igym.utils.ThrowUtils;
import com.leizi.igym.vo.TrainerVO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * @author PC
 * @description 针对表【t_trainer(教练表)】的数据库操作Service实现
 * @createDate 2024-04-07 08:44:33
 */
@Service
@Slf4j
public class TrainerServiceImpl extends ServiceImpl<TrainerMapper, Trainer>
        implements TrainerService {

    @Resource
    private RedisTemplate<String,String> redisTemplate;

    @Resource
    private TrainerMapper trainerMapper;

    @Resource
    private TrainerCategoryService trainerCategoryService;

    @Resource
    private CategoryService categoryService;

    /**
     * 管理端新增教练信息
     *
     * @param dto
     * @return: boolean
     **/
    @Override
    @Transactional
    public boolean saveInAdmin(TrainerAddDTO dto) {
        if (Objects.isNull(dto)) {
            throw new BusinessException(ReturnCodeEnum.PARAMS_ERROR);
        }

        String trName = dto.getTrName();
        LocalDate birthday = dto.getBirthday();
        String avatar = dto.getAvatar();
        List<Long> expertiseList = dto.getExpertiseList();
        Integer gender = dto.getGender();
        String intro = dto.getIntro();
        String idNo = dto.getIdNo();
        String phone = dto.getPhone();
        Integer career = dto.getCareer();

        List<Long> allId = categoryService.getAllId();
        if (CollectionUtil.isEmpty(allId)) {
            throw new BusinessException(ReturnCodeEnum.SYSTEM_ERROR, "无分类消息");
        }

        // 判断参数合法性
        if (StringUtils.isAnyBlank(trName, avatar, intro, idNo, phone)) {
            throw new BusinessException(ReturnCodeEnum.PARAMS_ERROR);
        }

        if (!CollectionUtil.containsAll(allId, expertiseList)) {
            throw new BusinessException(ReturnCodeEnum.PARAMS_ERROR);
        }

        if (Objects.isNull(gender) || !CommonConstant.GENDER_LIST.contains(gender)) {
            throw new BusinessException(ReturnCodeEnum.PARAMS_ERROR);
        }

        if (birthday == null) {
            throw new BusinessException(ReturnCodeEnum.PARAMS_ERROR);
        }

        if (Objects.isNull(career) || career < 0) {
            throw new BusinessException(ReturnCodeEnum.PARAMS_ERROR);
        }

        // 构造插入对象
        Trainer trainer = new Trainer();
        BeanUtils.copyProperties(dto, trainer);
        // 默认管理端添加是已认证了
        trainer.setIsIdentified(IdentifyConstant.IDENTIFIED);
        trainer.setJoinTime(LocalDate.now());
        Integer result = this.getBaseMapper().insert(trainer);
        if (null == result || result < 1) {
            return false;
        }
        Long trainerId = trainer.getId();

        // 向教练-擅长方向库插入信息
        List<TrainerCategory> trainerCategoryList = expertiseList.stream().map(expertise -> {
            TrainerCategory trainerCategory = new TrainerCategory();
            trainerCategory.setTrainerId(trainerId);
            trainerCategory.setCategoryId(expertise);
            return trainerCategory;
        }).toList();

        boolean bool = trainerCategoryService.saveBatch(trainerCategoryList);
        if (bool) {
            this.saveAllToRedis();
        }

        return bool;
    }

    /**
     * 管理端删除教练信息
     *
     * @param id
     * @return: boolean
     **/
    @Transactional
    @Override
    public boolean deleteTrainerInAdmin(Long id) {
        ThrowUtils.throwIf(Objects.isNull(id) || id <= 0, ReturnCodeEnum.PARAMS_ERROR);

        // 先删除教练擅长库记录
        QueryWrapper<TrainerCategory> qw = new QueryWrapper<>();
        qw.eq("trainer_id", id);
        boolean bool = trainerCategoryService.remove(qw);
        if (!bool) {
            return false;
        }

        // 删除教练
        Integer result = this.getBaseMapper().deleteById(id);
        if (null != result && result >= 1) {
            this.saveAllToRedis();
        }
        return null != result && result >= 1;
    }

    /**
     * 管理端批量删除教练
     *
     * @param ids
     * @return: boolean
     **/
    @Transactional
    @Override
    public boolean deleteTrainerBatchInAdmin(List<Long> ids) {
        ThrowUtils.throwIf(CollectionUtil.isEmpty(ids), ReturnCodeEnum.PARAMS_ERROR);
        List<TrainerCategory> trainerCategoryList = ids.stream().map(id -> {
            TrainerCategory trainerCategory = new TrainerCategory();
            trainerCategory.setTrainerId(id);
            return trainerCategory;
        }).toList();
        boolean bool = trainerCategoryService.removeBatchByIds(trainerCategoryList);
        if (!bool) {
            return false;
        }
        Integer result = this.getBaseMapper().deleteBatchIds(ids);
        if (null != result && result >= 1) {
            this.saveAllToRedis();
        }
        return null != result && result >= 1;
    }

    /**
     * 管理端修改教练信息
     *
     * @param dto
     * @return: boolean
     **/
    @Override
    @Transactional
    public boolean updateTrainer(TrainerDTO dto) {
        ThrowUtils.throwIf(Objects.isNull(dto), ReturnCodeEnum.PARAMS_ERROR);
        Trainer trainer = new Trainer();
        BeanUtils.copyProperties(dto, trainer);
        // 更新教练信息
        Integer result = this.getBaseMapper().updateById(trainer);
        if (Objects.isNull(result) || result <= 0) {
            return false;
        }

        // 更新擅长方向信息。采取先删在新增的策略
        // 检验id合法性
        // 为空直接返回
        List<Long> expertiseList = dto.getExpertiseList();
        if (Objects.isNull(expertiseList)) {
            return true;
        }

        // 不为空则继续判断是否合法
        List<Long> categoryAllId = categoryService.getAllId();
        Long trainerId = trainer.getId();


        boolean allIn = CollectionUtil.containsAll(categoryAllId, expertiseList);
        if (!allIn) {
            throw new BusinessException(ReturnCodeEnum.PARAMS_ERROR, "教练擅长方向有误！");
        }
        List<TrainerCategory> trainerCategories = expertiseList.stream().map(expertise -> {
            TrainerCategory trainerCategory = new TrainerCategory();
            trainerCategory.setTrainerId(trainerId);
            trainerCategory.setCategoryId(expertise);
            return trainerCategory;
        }).toList();

        QueryWrapper<TrainerCategory> qw = new QueryWrapper<>();
        qw.eq("trainer_id", trainerId);
        trainerCategoryService.remove(qw);
        boolean bool = trainerCategoryService.saveBatch(trainerCategories);
        if (bool) {
            this.saveAllToRedis();
        }
        return bool;
    }

    /**
     * 管理端分页查询教练信息
     *
     * @param dto
     * @return: Page<com.leizi.igym.vo.TrainerVO>
     **/
    @Override
    public Page<TrainerVO> getTrainerPage(TrainerPageQueryDTO dto) {
        ThrowUtils.throwIf(Objects.isNull(dto), ReturnCodeEnum.PARAMS_ERROR);

        // 创建条件查询器
        String trName = dto.getTrName();
        String phone = dto.getPhone();
        Integer isIdentify = dto.getIsIdentify();
        LocalDate beginTime = dto.getBeginTime();
        LocalDate endTime = dto.getEndTime();
        Long expertise = dto.getExpertise();

        QueryWrapper<TrainerVO> qw = new QueryWrapper<>();


        qw.like(StringUtils.isNotBlank(trName), "tr_name", trName);
        qw.like(StringUtils.isNotBlank(phone), "phone", phone);
        qw.eq(EmployeeConstant.EMP_IDENTIFY_LIST.contains(isIdentify), "isIdentified", isIdentify);
        DateQWUtils.getDateQWForTrainer(qw, beginTime, endTime);

        //  排序规则
        // qw.orderByDesc("id");
        // qw.orderByDesc("isIdentified");
        // qw.orderByDesc(TrainerConstant.TABLE_TRAINER_ALIAS + "." + "update_time");

        // 根据擅长方向id筛选出满足条件的教练id集合
        if (expertise != null) {
            List<Long> ids = trainerCategoryService.getTrainerByCategory(expertise);
            if (ids == null || ids.size() == 0) {
                return null;
            }
            dto.setIds(ids);
        }

        // Page<TrainerVO> voPage = trainerMapper.selectPageWithExpertise(new Page<>(dto.getCurrent(), dto.getPageSize()
        //         , false), qw);
        Page<TrainerVO> voPage1 = trainerMapper.selectPageWithExpertiseByDTO(new Page<>(dto.getCurrent(),
                dto.getPageSize()), dto);
        return voPage1;
    }

    /**
     * 双端根据id查询教练信息
     *
     * @param id
     * @return: com.leizi.igym.vo.TrainerVO
     **/
    @Override
    public TrainerVO getByIdWithExpertise(Long id) {
        ThrowUtils.throwIf(Objects.isNull(id) || id <= 0,
                ReturnCodeEnum.PARAMS_ERROR);
        // String jsonStr = redisTemplate.opsForValue().getBySearch(TrainerConstant.TRAINER_REDIS_KEY_PREFIX + id);
        // if (StringUtils.isNotBlank(jsonStr)) {
        //     return JSON.parseObject(jsonStr,TrainerVO.class);
        // }
        TrainerVO trainerVO =RedisUtils.get(TrainerConstant.TRAINER_REDIS_KEY_PREFIX + id, TrainerVO.class) ;
        if (Objects.nonNull(trainerVO)){
            log.info("根据id从redis中获取教练信息，信息为：{}",trainerVO);
            return trainerVO;
        }
        trainerVO = trainerMapper.selectByIdWithExpertise(id);
        // redisTemplate.opsForValue().set(TrainerConstant.TRAINER_REDIS_KEY_PREFIX + id, JSON.toJSONString(trainerVO));
        RedisUtils.set(TrainerConstant.TRAINER_REDIS_KEY_PREFIX + id,trainerVO);
        return trainerVO;
    }

    /**
     * 管理端修改教练认证情况
     *
     * @param id
     * @param isIdentify
     * @return: boolean
     **/
    @Override
    public boolean identifyTrainer(Long id, Integer isIdentify) {
        // 校验参数id是否合法
        ThrowUtils.throwIf(Objects.isNull(id) || id < 0, ReturnCodeEnum.PARAMS_ERROR);
        // 校验isIdentify参数是否合法
        ThrowUtils.throwIf(Objects.isNull(isIdentify) ||
                !Arrays.asList(TrainerConstant.NOT_IDENTIFY, TrainerConstant.IDENTIFIED)
                        .contains(isIdentify), ReturnCodeEnum.PARAMS_ERROR);

        Trainer dbTrainer = this.getBaseMapper().selectById(id);
        ThrowUtils.throwIf(Objects.isNull(dbTrainer), ReturnCodeEnum.PARAMS_ERROR);
        ThrowUtils.throwIf(dbTrainer.getIsIdentified() == 1, ReturnCodeEnum.IDENTIFY_ERROR);
        dbTrainer.setIsIdentified(isIdentify);
        dbTrainer.setJoinTime(LocalDate.now());
        if (this.getBaseMapper().updateById(dbTrainer) > 0) {
            this.saveAllToRedis();
        }
        return this.getBaseMapper().updateById(dbTrainer) > 0;
    }


    /**
     * 用户端 获取全部教练信息
     *
     * @param
     * @return: java.util.List<com.leizi.igym.vo.TrainerVO>
     **/
    @Override
    public List<TrainerVO> getAll() {
        String jsonStr = redisTemplate.opsForValue().get(TrainerConstant.REDIS_ALL_TRAINER_KEY);
        if (StringUtils.isNotBlank(jsonStr)){
            List list = JSON.parseObject(jsonStr, List.class);
            return list;
        }
        List<TrainerVO> trainerVOList = this.getBaseMapper().selectAllWithExpertise();
        redisTemplate.opsForValue().set(TrainerConstant.REDIS_ALL_TRAINER_KEY, JSON.toJSONString(trainerVOList));
        return trainerVOList;
    }


    private void saveAllToRedis() {
        List<TrainerVO> trainerVOList = this.getBaseMapper().selectAllWithExpertise();
        redisTemplate.opsForValue().set(TrainerConstant.REDIS_ALL_TRAINER_KEY, JSON.toJSONString(trainerVOList));
    }


}




